package errors

import (
	"fmt"
	"runtime"
	"strconv"

	"xp/lib/pb"
)

var ignorePrefix = []string{"lib/", "vendor/"}

var (
	codeMap   = make(map[Code]string)
	stringMap = make(map[Code]string)
)

func RegisterCodeMap(m map[Code]string) {
	for code, desc := range m {
		codeMap[code] = desc
		stringMap[code] = strconv.Itoa(int(code))
	}
}

type Code int

func (c Code) Error() string {
	if desc, ok := codeMap[c]; ok {
		return desc
	}
	return fmt.Sprintf("<%d>", c)
}

func (c Code) String() string {
	if code, ok := stringMap[c]; ok {
		return code
	}
	return strconv.Itoa(int(c))
}

func (c Code) Bytes() []byte {
	var s = pb.EncodeLen(uint64(c)) + 1
	var buf = make([]byte, s)
	buf[0] = 0x8
	pb.EncodeVarint(buf, 1, uint64(c))
	return buf
}

type Verbose struct {
	Code Code
	Desc string
}

func (v *Verbose) Error() string {
	return v.Desc
}

func (v *Verbose) Bytes() []byte {
	var i, s, l int
	s += pb.EncodeLen(uint64(v.Code)) + 1
	l = len(v.Desc)
	if l > 0 {
		s += pb.EncodeLen(uint64(l)) + l + 1
	}
	var buf = make([]byte, s)
	buf[0] = 0x8
	i = pb.EncodeVarint(buf, 1, uint64(v.Code))
	if l > 0 {
		buf[i] = 0x12
		i++
		i = pb.EncodeVarint(buf, i, uint64(l))
		copy(buf[i:], v.Desc)
	}
	return buf
}

func New(desc string) error {
	return &Verbose{Code: CodeInternal, Desc: desc}
}

func NewCode(code int, desc string) error {
	return &Verbose{Code: Code(code), Desc: desc}
}

func BadRequest(desc string) error {
	return &Verbose{Code: CodeBadRequest, Desc: desc}
}

type Stack struct {
	cause   error
	callers string
}

func (s *Stack) Error() string {
	return s.cause.Error()
}

func (s *Stack) Cause() error {
	return s.cause
}

func (s *Stack) Trace() string {
	return s.callers
}

func Wrap(err error) error {
	if err == nil {
		return nil
	}
	const depth = 3
	var pcs = make([]uintptr, depth)
	runtime.Callers(2, pcs)
	return &Stack{err, getCallers(pcs)}
}

func Cause(err error) error {
	type causer interface {
		Cause() error
	}

	for err != nil {
		cause, ok := err.(causer)
		if !ok {
			break
		}
		err = cause.Cause()
	}
	return err
}

func Equal(err error) bool {
	return Cause(err) == err
}

func ToBytes(err error) []byte {
	if err == nil {
		return nil
	}
	err = Cause(err)
	if code, ok := err.(Code); ok {
		return code.Bytes()
	}
	verbose, ok := err.(*Verbose)
	if !ok {
		verbose = &Verbose{Code: CodeInternal, Desc: err.Error()}
	}
	return verbose.Bytes()
}

func FromBytes(data []byte) error {
	if len(data) < 2 {
		return nil
	}
	if data[0] != 0x8 {
		return nil
	}
	var (
		desc       string
		i, p, code int
	)
	for {
		// 这里的处理方式只适合特定的类型，如果修改结构请根据实际情况修改
		s, n, l := uint(0), uint64(0), 0
		for j, b := range data[i+1:] {
			if b < 0x80 {
				if j > 9 || j == 9 && b > 1 {
					return nil
				}
				n |= uint64(b) << s
				l = j + 1
				break
			}
			n |= uint64(b&0x7f) << s
			s += 7
		}
		switch data[i] {
		case 0x8:
			i += l + 1
			code = int(n)
			if i == len(data) {
				return Code(code)
			}
		case 0x12:
			i += l + 1
			p = i + int(n)
			desc = string(data[i:p])
			return &Verbose{Code: Code(code), Desc: desc}
		}
	}
}
